ESP32之OTA

技术经验 dingxiao 阅读数:3745 2019年10月14日 09:13


ESP32之OTA

0x01-OTA

OTA是Over the Air的缩写,顾名思义通过空中方式升级,所谓“空中”指的是远程无线方式,OTA可以理解为一种远程无线升级技术。在实际产品开发过程中,在线升级可以远程解决产品软件开发引入的问题,更好地满足用户需求。

0x02-ESP32的OTA升级方式

ESP32的OTA更新是使用 Wi-Fi 连接而不是串行端口将固件加载到 ESP 模块的过程。主要有三种方式可以实现OTA功能:

 1)Arduino IDE:主要用于软件开发阶段,实现不接线固件烧写
 2)Web Browser:通过 Web 浏览器手动提供应用程序更新模块
 3)HTTP Server:自动使用http服务器 - 针对产品应用

其中以第3种方式作为产品级OTA方式更为方便可行。

0x03-OTA使用HTTP Server方式升级

参考网址:

 https://blog.csdn.net/liwei16611/article/details/81051909

ESP32 连接 HTTP 服务器,发送请求 Get 升级固件;每次读取1KB固件数据,写入Flash。

ESP32 SPI Flash 内有与升级相关的(至少)四个分区:OTA data、Factory App、OTA_0、OTA_1。其中 FactoryApp 内存有出厂时的默认固件。

首次进行 OTA 升级时,OTA Demo 向 OTA_0 分区烧录目标固件,并在烧录完成后,更新 OTA data 分区数据并重启。

系统重启时获取 OTA data 分区数据进行计算,决定此后加载 OTA_0 分区的固件执行(而不是默认的 Factory App 分区内的固件),从而实现升级。

同理,若某次升级后 ESP32 已经在执行 OTA_0 内的固件,此时再升级时 OTA Demo 就会向 OTA_1 分区写入目标固件。再次启动后,执行 OTA_1 分区实现升级。以此类推,升级的目标固件始终在 OTA_0、OTA_1 两个分区之间交互烧录,不会影响到出厂时的 Factory App 固件。

上图对应ESP32模块,可以方便的理解ESP32为什么配置了4MB的Flash空间。

0x04-参考代码
 #include <WiFi.h>
 #include <Update.h>
 
 WiFiClient client;
 
 // Variables to validate
 // response from S3
 int contentLength = 0;
 bool isValidContentType = false;
 
 // Your SSID and PSWD that the chip needs
 // to connect to
 const char* SSID = "YOUR-SSID";
 const char* PSWD = "YOUR-SSID-PSWD";
 
 // S3 Bucket Config
 String host = "bucket-name.s3.ap-south-1.amazonaws.com"; // Host => bucket-name.s3.region.amazonaws.com
 int port = 80; // Non https. For HTTPS 443. As of today, HTTPS doesn't work.
 String bin = "/sketch-name.ino.bin"; // bin file name with a slash in front.
 
 // Utility to extract header value from headers
 String getHeaderValue(String header, String headerName) {
   return header.substring(strlen(headerName.c_str()));
 }
 
 // OTA Logic
 void execOTA() {
   Serial.println("Connecting to: " + String(host));
   // Connect to S3
   if (client.connect(host.c_str(), port)) {
     // Connection Succeed.
     // Fecthing the bin
     Serial.println("Fetching Bin: " + String(bin));
 
     // Get the contents of the bin file
     client.print(String("GET ") + bin + " HTTP/1.1\r\n" +
                  "Host: " + host + "\r\n" +
                  "Cache-Control: no-cache\r\n" +
                  "Connection: close\r\n\r\n");
 
     // Check what is being sent
     //    Serial.print(String("GET ") + bin + " HTTP/1.1\r\n" +
     //                 "Host: " + host + "\r\n" +
     //                 "Cache-Control: no-cache\r\n" +
     //                 "Connection: close\r\n\r\n");
 
     unsigned long timeout = millis();
     while (client.available() == 0) {
       if (millis() - timeout > 5000) {
         Serial.println("Client Timeout !");
         client.stop();
         return;
       }
     }
     // Once the response is available,
     // check stuff
 
     /*
        Response Structure
         HTTP/1.1 200 OK
         x-amz-id-2: NVKxnU1aIQMmpGKhSwpCBh8y2JPbak18QLIfE+OiUDOos+7UftZKjtCFqrwsGOZRN5Zee0jpTd0=
         x-amz-request-id: 2D56B47560B764EC
         Date: Wed, 14 Jun 2017 03:33:59 GMT
         Last-Modified: Fri, 02 Jun 2017 14:50:11 GMT
         ETag: "d2afebbaaebc38cd669ce36727152af9"
         Accept-Ranges: bytes
         Content-Type: application/octet-stream
         Content-Length: 357280
         Server: AmazonS3
                                   
         {{BIN FILE CONTENTS}}
     */
     while (client.available()) {
       // read line till /n
       String line = client.readStringUntil('\n');
       // remove space, to check if the line is end of headers
       line.trim();
 
       // if the the line is empty,
       // this is end of headers
       // break the while and feed the
       // remaining `client` to the
       // Update.writeStream();
       if (!line.length()) {
         //headers ended
         break; // and get the OTA started
       }
 
       // Check if the HTTP Response is 200
       // else break and Exit Update
       if (line.startsWith("HTTP/1.1")) {
         if (line.indexOf("200") < 0) {
           Serial.println("Got a non 200 status code from server. Exiting OTA Update.");
           break;
         }
       }
 
       // extract headers here
       // Start with content length
       if (line.startsWith("Content-Length: ")) {
         contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str());
         Serial.println("Got " + String(contentLength) + " bytes from server");
       }
 
       // Next, the content type
       if (line.startsWith("Content-Type: ")) {
         String contentType = getHeaderValue(line, "Content-Type: ");
         Serial.println("Got " + contentType + " payload.");
         if (contentType == "application/octet-stream") {
           isValidContentType = true;
         }
       }
     }
   } else {
     // Connect to S3 failed
     // May be try?
     // Probably a choppy network?
     Serial.println("Connection to " + String(host) + " failed. Please check your setup");
     // retry??
     // execOTA();
   }
 
   // Check what is the contentLength and if content type is `application/octet-stream`
   Serial.println("contentLength : " + String(contentLength) + ", isValidContentType : " + String(isValidContentType));
 
   // check contentLength and content type
   if (contentLength && isValidContentType) {
     // Check if there is enough to OTA Update
     bool canBegin = Update.begin(contentLength);
 
     // If yes, begin
     if (canBegin) {
       Serial.println("Begin OTA. This may take 2 - 5 mins to complete. Things might be quite for a while.. Patience!");
       // No activity would appear on the Serial monitor
       // So be patient. This may take 2 - 5mins to complete
       size_t written = Update.writeStream(client);
 
       if (written == contentLength) {
         Serial.println("Written : " + String(written) + " successfully");
       } else {
         Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?" );
         // retry??
         // execOTA();
       }
 
       if (Update.end()) {
         Serial.println("OTA done!");
         if (Update.isFinished()) {
           Serial.println("Update successfully completed. Rebooting.");
           ESP.restart();
         } else {
           Serial.println("Update not finished? Something went wrong!");
         }
       } else {
         Serial.println("Error Occurred. Error #: " + String(Update.getError()));
       }
     } else {
       // not enough space to begin OTA
       // Understand the partitions and
       // space availability
       Serial.println("Not enough space to begin OTA");
       client.flush();
     }
   } else {
     Serial.println("There was no content in the response");
     client.flush();
   }
 }
 
 void setup() {
   //Begin Serial
   Serial.begin(115200);
   delay(10);
 
   Serial.println("Connecting to " + String(SSID));
 
   // Connect to provided SSID and PSWD
   WiFi.begin(SSID, PSWD);
 
   // Wait for connection to establish
   while (WiFi.status() != WL_CONNECTED) {
     Serial.print("."); // Keep the serial monitor lit!
     delay(500);
   }
 
   // Connection Succeed
   Serial.println("");
   Serial.println("Connected to " + String(SSID));
 
   // Execute OTA Update
   execOTA();
 }
 
 void loop() {
   // chill
 }



captcha
    暂无评论